import { DataSyncWatcher } from './../../../../../common/dataSync/dataSyncWatcher';
import { SyncStatus } from './../../modules/services/sync-status';
import { config } from '../../modules/config';
import { logger } from '../../modules/logger';
var os = require('os');
var path = require('path');
var express = require('express');
var extend = require('extend');
var moment = require('moment');
var lingo = require('lingo');
var lodash = require('lodash');

var router = express.Router();

var utils = require('../../modules/utils');
var setting = require('../../modules/services/settings-service');
var resources = require('../../modules/services/resources-service');
var uploads = require('../../modules/services/uploads-service');
var updates = require('../../modules/services/updates-service');

function assertStoreId(storeId) {
    //因为连锁的原因不校验是否是当前店铺
    // if (config.storeId != storeId)
    //     throw invalidParams('invalid store');
}

// 检查是否是pending状态

function accessableCheck(req, res, next) {
    if (SyncStatus.Current.isReady()) {
        next();
    } else {
        res.status(503).json({});
        res.end();
    }
}

router.post('/ready/:status', function (req, res, next) {
    var status = req.params.status;
    if (status == '200') {
        SyncStatus.Current.setReady();
    } else if (status == '503') {
        SyncStatus.Current.setPending();
    } else if (status == '403') {
        SyncStatus.Current.setForbid();
    } else {
        res.status(503);
    }
    res.json({});
});


router.get('/heartbeat.json', accessableCheck, function (req, res, next) {
    if (!req.query.t) {
        res.status(404).json({});
    } else {
        res.json({});
    }
});

router.get('/about', function (req, res, next) {
    return updates.datastore.count({}).then(function (updatesCount) {
        return uploads.datastore.count({}).then(function (uploadsCount) {
            res.json({
                version: config.version,
                storeId: config.storeId,
                ldc: {
                    id: lodash.result(config.options, 'device.id'),
                    host: lodash.result(config.options, 'device.host'),
                    terminal: lodash.result(config.options, 'terminal')
                }
            });
        });
    });
});

router.get('/status', function (req, res, next) {
    return updates.datastore.count({}).then(function (updatesCount) {
        return uploads.datastore.count({ packId: null }).then(function (uploadsCount) {
            res.json({
                'pending': SyncStatus.Current.isPending(),
                cloud: {
                    lastSyncTime: setting.lastSyncTimeKey,
                    updates: DataSyncWatcher.Current.totalCounter,
                    uploads: uploadsCount
                },
                extend: {
                    system: {
                        hostname: os.hostname(),
                        uptime: os.uptime(),
                        totalmem: os.totalmem(),
                        freemem: os.freemem()
                    }
                }
            });
        });
    });
});

router.get('/users/:userId', accessableCheck, function (req, res, next) {
    return resources.getAsUpdate('users', req.params.userId).then(function (resource) {
        sendResource(res, resource);
    });
});

router.get('/stores/:storeId/timestamp', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    res.json({ timestamp: new Date().getTime() });
});

/**
 * getUpdates
 */
router.get('/stores/:storeId/updates/:timeAfter', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    var timeAfter = req.params.timeAfter == 'all' ? 0 : moment(req.params.timeAfter, 'YYYYMMDDHHmmssSSS').valueOf();
    var excludedBindId = req.query.excludedBindId;
    var expire = req.query.expire;

    return resources.queryAsUpdates(timeAfter, excludedBindId, expire).then(function (updates) {
        // if (updates.urls.length) {
        //     logger.log(JSON.stringify({ url: req.url, data: updates }));
        // }
        res.json(updates);
    });
});

function sendResource(res, resource) {
    if (resource) {
        if (resource.data) {
            res.json(resource.data);
        } else {
            res.status(400).json({ message: resource.state == 1 ? '资源被删除了' : '资源数据不正常' });
        }
    } else {
        res.status(400).json({ message: '资源在LDC上不存在' });
    }
}

/**
 * get inventories's  soldOut
 */
router.get('/stores/:storeId/inventories/soldOut/:resId', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    return resources.getAsUpdate('inventories/soldOut', req.params.resId).then(function (resource) {
        sendResource(res, resource);
    });
});

/**
 * get inventories's  Update
 */
router.get('/stores/:storeId/inventories/:resId/pos', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    return resources.getAsUpdate('inventories', req.params.resId).then(function (resource) {
        sendResource(res, resource);
    });
});

/**
 * get inventories's abbreviation Update
 */
router.get('/stores/:storeId/inventories/:resId/abbreviation', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    return resources.getAsUpdate('inventories', req.params.resId).then(function (resource) {
        if (resource) {
            sendResource(res, { value: resource.searchCode || '' });
        } else {
            sendResource(res, undefined);
        }
    });
});

/**
 * get the other resource Update
 */
router.get('/stores/:storeId/:resEntity/:resId', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    var promise = null;
    if (req.headers['mpj-sync-single-package']) {
        promise = resources.getSinglePackageAsUpdate(req.params.resEntity, req.params.resId);
    } else {
        promise = resources.getAsUpdate(req.params.resEntity, req.params.resId);
    }
    return promise.then(function (resource) {
        sendResource(res, resource);
    })
});

/**
 * get the store Update
 * 这种是取店的数据
 */
router.get('/stores/:storeId', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    return resources.getAsUpdate('stores', req.params.storeId).then(function (resource) {
        sendResource(res, resource);
    });
});

router.get('/getStoreInfo', accessableCheck, function (req, res, next) {
    return resources.getAsUpdate('stores', config.storeId).then(function (resource) {
        sendResource(res, resource);
    });
});

function getSyncURIFromPUTReq(req, other?) {
    var url = '/api/stores/' + req.params.storeId + '/' + req.params.resEntity + '/' + req.params.resId;
    lodash.forEach(other, function (item) {
        url += item;
    });
    return url;
}

function getSyncStateFromPUTReq(req) {
    return req.params.hasOwnProperty('state') ? req.params.state : 0;
}

function newResourceFromPUTReq(req) {
    var resId = req.params.resId;
    var resource: any = {
        updateId: resId,
        id: resId,
        state: getSyncStateFromPUTReq(req),
        uri: getSyncURIFromPUTReq(req),
        data: req.body
    };

    // 先从配置中查找是否有特殊的
    var resEntity = req.params.resEntity;
    var type = lodash.result(config.spUrl2Type, resEntity);
    if (type) {
        resource.type = type;
    } else {
        resource.type = lingo.en.singularize(resEntity);
    }

    if (req.headers['mpj-sync-time-expire']) {
        resource.timeExpire = parseInt(req.headers['mpj-sync-time-expire']);
    }
    if (req.headers['mpj-sync-resource-version']) {
        resource.version = parseInt(req.headers['mpj-sync-resource-version']);
    }
    if (req.headers['mpj-sync-terminal-bind-id']) {
        resource.terminalBindId = req.headers['mpj-sync-terminal-bind-id']
    }
    return resource;
}

function getIsNoSendNotificationFromPUTReq(req) {
    return req.headers['mpj-sync-packed'] && req.params.resEntity != 'sync-packages'
}

function catchHttpError(res) {
    return function (err) {
        if (!err.conflicts) {
            logger.error(err);
            logger.error(err.stack);
        }
        if (err.message) {
            err.msg = err.message;
            delete err.message;
        }
        res.status(412).json(err);
    }
}

router.put('/stores/:storeId/:resEntity/:resId/unbound', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    return uploads.saveUploads(getSyncURIFromPUTReq(req, ['/unbound']), req.params, req.headers, res.body).then(function () {
        return resources.unboundUpdate(req.params.resId);
    }).then(function () {
        res.json({});
    }).catch(catchHttpError(res));
});

router.put('/stores/:storeId/:resEntity/:resId/delete', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    var uri = getSyncURIFromPUTReq(req);
    return uploads.saveUploads(uri + '/delete', req.params, req.headers, req.body).then(function () {
        var update: any = {
            id: req.params.resId,
            updateId: req.params.resId,
            type: req.params.resEntity,
            uri: uri
        };
        if (req.headers['mpj-sync-time-expire']) {
            update.timeExpire = parseInt(req.headers['mpj-sync-time-expire']);
        }
        if (req.headers['mpj-sync-terminal-bind-id']) {
            update.terminalBindId = req.headers['mpj-sync-terminal-bind-id']
        }
        return resources.logicDeleteById(update);
    }).then(function () {
        res.json({});
    }).catch(catchHttpError(res));
});

function doResourceSave(req, noEvent?) {
    var promise = null;
    if (req.headers['mpj-sync-resource-version']) {
        promise = resources.isOutOfVersion(req.params.resId, parseInt(req.headers['mpj-sync-resource-version']));
    } else {
        promise = Promise.resolve(false);
    }
    return promise.then(function (isExpired) {
        if (isExpired)
            return;
        return uploads.saveUploads(getSyncURIFromPUTReq(req), req.params, req.headers, req.body).then(function () {
            if (req.params.resEntity != 'workshifts') {
                return resources.putUpdate(newResourceFromPUTReq(req), getIsNoSendNotificationFromPUTReq(req), noEvent);
            }
        });
    });
}

function doResourcesPut(req, res, next, noEvent?) {
    var promise = null;
    if (req.headers['mpj-sync-single-package']) {
        var children = req.body.children;
        delete req.body.children;
        var syncInfo = {
            excludedBindId: req.headers['mpj-sync-terminal-bind-id'],
            lastSyncTime: req.headers['mpj-sync-last-sync-time']
        };
        promise = resources.lockReturnFull(children.map(function (child) {
            return { id: child.obj.id, version: lodash.get(child.obj, 'extended.version.version') };
        }), req.body.terminalId, req.body.userName, syncInfo).then(function (versions) {
            return Promise.all(children.map(function (child, index) {
                resources.initExtended(child.obj);
                child.obj.extended.version = lodash.omit(versions.versions[index], 'stale');
                var _type = lodash.result(config.type2spUrl, child.type);
                return doResourceSave({
                    params: {
                        storeId: req.params.storeId,
                        resEntity: _type || child.type,
                        resId: child.obj.id
                    },
                    headers: {
                        'mpj-sync-time-expire': child.timeExpire,
                        'mpj-sync-resource-version': child.resourceVersion,
                        'mpj-sync-packed': req.body.id,
                        'mpj-sync-terminal-bind-id': req.headers['mpj-sync-terminal-bind-id']
                    },
                    body: child.obj
                }, noEvent);
            })).then(function () {
                req.body.resources = lodash.map(children, function (child) {
                    return { id: child.obj.id, uri: child.uri, type: child.type };
                });
                return doResourceSave(req, noEvent);
            })
        });
    } else {
        promise = doResourceSave(req, noEvent);
    }
    return promise.then(function () {
        res.json({});
    }).catch(catchHttpError(res));
}

function invalidParams(msg) {
    return { msg: msg, status: 406 };
}

router.put('/stores/:storeId/inventories/soldOut/:resId', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    if (!req.body) {
        throw invalidParams('没有内容');
    }
    req.params.resEntity = 'inventories/soldOut';
    req.params.state = req.body.disabled ? 1 : 0;
    return doResourcesPut(req, res, next);
});

/**
 * resEntity是复数,type是单数(sync-packages例外)
 */
router.put('/stores/:storeId/:resEntity/:resId', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    if (!req.body) {
        throw invalidParams('没有内容');
    }
    return doResourcesPut(req, res, next);
});

/**
 * 日志
 */
var fs = require('fs');
router.put('/stores/:storeId/log', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    if (!req.body) {
        throw invalidParams('没有内容');
    }
    if (!req.body.terminalId) {
        throw invalidParams('没有提供terminalId');
    }
    function idToName(id) {
        return resources.datastore.get({ id: id }).then(function (resource) {
            var name = lodash.get(resource, 'data.name') || id;
            return name;
        });
    }
    return Promise.all(req.body.resources.map(function (entity) {
        return idToName(req.body.terminalId).then(function (terminalName) {
            var p = path.join(process.cwd(), '/logs/' + req.params.storeId + '/operations/' + terminalName + '/' + entity.type);
            utils.mkdirsSync(p);
            var p1 = path.join(process.cwd(), '/logs/' + req.params.storeId + '/operations/all/' + entity.type);
            utils.mkdirsSync(p1);
            return idToName(entity.id).then(function (name) {
                return (<any>Promise).promisify(fs.appendFile)(path.join(p, name + '.txt'), moment().toString() + ' ' + req.body.operation + ' ' + JSON.stringify(req.body.data) + '\n').then(function () {
                    return (<any>Promise).promisify(fs.appendFile)(path.join(p1, name + '.txt'), moment().toString() + ' ' + req.body.operation + ' ' + terminalName + ' ' + JSON.stringify(req.body.data) + '\n');
                });
            });
        })
    })).then(function () {
        res.json({});
    }).catch(function (err) {
        console.log(err);
        res.status(406).json(lodash.isObject(err) ? err : { msg: err });
    });
});


/**
 * 锁定传入的资源
 */
router.post('/stores/:storeId/resources/lock', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    if (!req.body) {
        throw invalidParams('没有内容');
    }
    var body = req.body;
    if (!body.terminalId) {
        throw invalidParams('没有指定终端Id');
    }
    if (!body.userName) {
        throw invalidParams('没有指定用户');
    }
    var version = req.headers['mpj-app-version'];
    resources.lock(body.resourceVersions, body.terminalId, body.userName, lodash.pick(body, ['lastSyncTime', 'excludedBindId'])).then(function (result) {
        if (!version) {
            result = result.versions;
        }
        res.json(result);
    }).catch(function (err) {
        //        var appName = req.headers['mpj-app-name'];
        //        var oldClerk = (appName=='clerk'&&Number(version)<=1.1);
        //        var oldPos = (appName=='pos'&&Number(version)<=46.3)

        if (!version) {
            err = err.conflicts;
        }
        res.status(406).json(lodash.isObject(err) ? err : { msg: err });
    });
});

/**
 * 锁定传入的资源
 */
router.post('/stores/:storeId/resources/unlock', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    if (!req.body) {
        throw invalidParams('没有内容');
    }
    var body = req.body;
    if (!body.terminalId) {
        throw invalidParams('没有指定终端Id');
    }
    resources.unlock(body.resourceVersions, body.terminalId).then(function (result) {
        res.json(result);
    }).catch(function (err) {
        res.status(406).json(lodash.isObject(err) ? err : { msg: err });
    });
});
/**
 * 锁定传入的资源
 */
router.post('/stores/:storeId/resources/check', accessableCheck, function (req, res, next) {
    assertStoreId(req.params.storeId);
    if (!req.body) {
        throw invalidParams('没有内容');
    }
    var body = req.body;
    resources.check(body.resourceVersions, body.terminalId, body.userName, lodash.pick(body, ['lastSyncTime', 'excludedBindId'])).then(function (result) {
        res.json(result);
    }).catch(function (err) {
        res.status(406).json(lodash.isObject(err) ? err : { msg: err });
    });
});

module.exports = router;
